package com.gyy.modbusMasterTcp;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSONObject;
import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.WriteMultipleRegistersRequest;
import com.digitalpetri.modbus.requests.WriteSingleCoilRequest;
import com.digitalpetri.modbus.requests.WriteSingleRegisterRequest;
import com.digitalpetri.modbus.responses.ModbusResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.util.concurrent.TimeUnit;

/***
 * @Author gmd
 * @Description modbus通讯，使用modbus-master-tcp框架写入slave数据
 * @Date 2022-01-03 17:10:02
 */
public class ModbusMasterTCPWrite {

    public static ModbusTcpMaster master;


    /**
     * 获取TCP协议的Master
     */
    public static void initModbusTcpMaster() {
        if (master == null) {
            // 创建配置
            ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("127.0.0.1").setPort(502).build();
            master = new ModbusTcpMaster(config);
        }
    }


    /**
     * 释放资源
     */
    public static void release() {
        if (master != null) {
            master.disconnect();
        }
        Modbus.releaseSharedResources();
    }


    /**
     * 写入线圈寄存器（0x01）数据  功能码为05
     *
     * @param slaveId 从机ID
     * @param address 数据起始地址
     * @param values  需要写入的数据
     */
    public static void writeCoil(int slaveId, int address, boolean values) throws Exception {
        ModbusResponse response = master.sendRequest(new WriteSingleCoilRequest(address++, values), slaveId)
                .get(10, TimeUnit.SECONDS);

        System.out.println("\n写入线圈寄存器（0x01）数据操作状态为：" + (response == null ? false : true));
    }


    /**
     * 批量写入线圈寄存器（0x01）数据  功能码为15
     *
     * @param slaveId 从机ID
     * @param address 数据起始地址
     * @param values  需要批量写入的数据
     */
    public static void writeCoils(int slaveId, int address, boolean[] values) throws Exception {
        boolean[] flags = new boolean[values.length];

        for (int i = 0; i < values.length; i++) {
            ModbusResponse response = master.sendRequest(new WriteSingleCoilRequest(address++, values[i]), slaveId)
                    .get(10, TimeUnit.SECONDS);

            if (response != null) {
                flags[i] = true;
            }
        }
        System.out.println("\n写入线圈寄存器（0x01）数据操作状态如下：" + JSONObject.toJSONString(flags));
    }


    /**
     * 写入保持寄存器（0x03）数据  功能码为06
     *
     * @param slaveId 从机ID
     * @param address 数据起始地址
     * @param values  需要写入的数据
     */
    public static <T extends Number> void writeHoldingRegister(int slaveId, int address, T values) throws Exception {
        Integer data = 0;

        if (values instanceof Integer) {
            data = (Integer) values;

        } else if (values instanceof Float) {
            data = Float.floatToIntBits((Float) values);

        } else if (values instanceof Double) {
            data = Long.valueOf(Double.doubleToRawLongBits((Double) values)).intValue();
        }

        ModbusResponse response = master.sendRequest(new WriteSingleRegisterRequest(address, data), slaveId)
                .get(10, TimeUnit.SECONDS);

        System.out.println("\n写入寄存器（0x03）数据操作状态为：" + (response == null ? false : true));
    }


    /**
     * 批量写入保持寄存器（0x03）数据  功能码为16
     *
     * @param slaveId 从机ID
     * @param address 数据起始地址
     * @param values  需要批量写入的数据
     */
    public static <T extends Number> void writeHoldingRegisters(int slaveId, int address, T[] values) throws Exception {
        ByteBuf data = valueConvertByte(values);

        ModbusResponse response = master.sendRequest(new WriteMultipleRegistersRequest(address, values.length, data), slaveId)
                .get(10, TimeUnit.SECONDS);

        System.out.println("\n批量写入寄存器（0x03）数据操作状态为：" + (response == null ? false : true));
    }


    public static <T extends Number> ByteBuf valueConvertByte(T[] objects) {
        byte[] result = new byte[0];
        if (objects[0] instanceof Integer) {
            for (int i = 0; i < objects.length; i++) {
                byte[] bytes = HexUtil.decodeHex(String.format("%04X", objects[i]));
                result = ArrayUtil.addAll(result, bytes);
            }

        } else if (objects[0] instanceof Float) {
            for (int i = 0; i < objects.length; i++) {
                byte[] bytes = HexUtil.decodeHex(String.format("%08X", Float.floatToIntBits((Float) objects[i])));
                result = ArrayUtil.addAll(result, bytes);
            }

        } else if (objects[0] instanceof Double) {
            for (int i = 0; i < objects.length; i++) {
                byte[] bytes = HexUtil.decodeHex(String.format("%016X", Double.doubleToLongBits((Double) objects[i])));
                result = ArrayUtil.addAll(result, bytes);
            }
        }

        return Unpooled.wrappedBuffer(result);
    }


    public static void main(String[] args) throws Exception {
        // 初始化资源
        initModbusTcpMaster();

        // 单个写入线圈寄存器数据
        writeCoil(1, 9, true);

        // 批量写入线圈寄存器数据
        writeCoils(1, 0, new boolean[]{true, true, true, true, false, false, true, true});

        // 单个写入保持寄存器数据
        writeHoldingRegister(6, 0, 123);

        // 批量写入保持寄存器数据，分为整数，单精度，双精度
        writeHoldingRegisters(6, 0, new Integer[]{65534, 16, 14, 11, 15, 18});
        writeHoldingRegisters(9, 0, new Float[]{12.12f, 16.189f, 14.14f, 65.23f, 123.23f});
        writeHoldingRegisters(3, 0, new Double[]{12.12d, 16.123456d});

        // 释放资源
        release();
    }


}
